/*
 *  levelUpGame - Ghost.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include "ofTypes.h"
#include "LevelUpApp.h"
#include "Settings.h"
#include "Player.h"
#include "Ghost.h"

const int Ghost::DEFAULT_FRAME_RATE = 6;
const float Ghost::COLLISION_SCALE = 0.45f;

Ghost::Ghost( const LevelUpApp& appRef_ )
: GridObject( appRef_ ), appRef( appRef_ ), s( Settings::getInstance() ),
  pathFinder( appRef_ ), speedFactor( 1.0f )
{ lastNearestPlayer = -1; /* TEMP */ }

void Ghost::setup( const string& path, const string& prefix ) {
	int frameRate = ofToInt( *s.getValue( "ghost_frame_rate", ofToString( DEFAULT_FRAME_RATE ) ) );
	
	seqPlayer.loadSequence( path, prefix );
	const ofPoint& size = seqPlayer.getSize();
	setSize( size );
	
	//FIXME: drawing gets scaled, but the bounding box does not know so...is this a problem? (goes for other object too)
	drawSizeFactor = game.getMaxObjectWidth() / size.x;
	
	seqPlayer.setFrameRate( frameRate );
	seqPlayer.play();
	
	setCollisionBBoxScale( COLLISION_SCALE );
}

//NOTE: FIXME: this function has been hacked to allow for stable (yet choppy) ghost movement
void Ghost::update( bool noMovement ) {
	float elapsed = game.getTime() - lastGameTime;
	
	//TEMP
	bool skipUpdate = ( elapsed < 1.0f / 40.0f );
	if ( skipUpdate ) {
		//ofLog( OF_LOG_NOTICE, "(Ghost.cpp) skipping update" );
		return;
	}
	
	seqPlayer.update();
	
	if ( ! noMovement ) {
//TEMP
//ofPoint oldPos = getPosition();
		const Ghost* og = 0;
		if ( balanceKind == BK_CULTURE ) { og = game.getIndustryGhost(); }
		else { og = game.getCultureGhost(); }
		const ofPoint* otherGhostPos = ( og ) ? &og->getPosition() : 0;
		
		lastPath.clear();
		lastNearestPlayer = findNearestPlayer( true );
		if ( lastNearestPlayer >= 0 ) {
			lastPath = pathFinder.findPath( getPosition(), game.getPlayers()[lastNearestPlayer]->getPosition(), otherGhostPos );
			
//if ( lastPath.size() > 0 ) {
//	ofPoint lastPathPos = getPosition();
//	string sd, sp = "(" + ofToString( lastPathPos.x, 2 ) + ", " + ofToString( lastPathPos.y, 2 ) + ")->";
//	for ( int i = 0; i < lastPath.size(); ++i ) {
//		sd += ofToString( pointDist( lastPathPos, lastPath[i] ), 2 ) + " ";
//		sp += "(" + ofToString( lastPath[i].x, 2 ) + ", " + ofToString( lastPath[i].y, 2 ) + ")";
//		lastPathPos = lastPath[i];
//	}
//	ofLog( OF_LOG_NOTICE, "===GHOST PATH pDist {%s} -- pPos {%s}", sd.c_str(), sp.c_str() );
//} else {
//	ofLog( OF_LOG_NOTICE, "===GHOST PATH no path found to player %i", lastNearestPlayer );
//}
			
			if ( lastPath.size() > 0 ) {
				float moveDist = baseSpeed * speedFactor * elapsed;
				int i = 0;
				
				while ( moveDist > 0.0f ) {
					float pathSegDist = pointDist( lastPath[i], getPosition() );
					if ( moveDist < pathSegDist ) {
						float factor = moveDist / pathSegDist;
//TEMP
//ofLog( OF_LOG_WARNING, "(Ghost.cpp) %c%2i: %.1f < %.1f; f=%.1f", balanceKind == BK_CULTURE ? 'C' : 'I', i, moveDist, pathSegDist, factor );
//ofPoint prevPos = getPosition();
						setPosition( ( lastPath[i] - getPosition() ) * factor + getPosition() );
//ofLog( OF_LOG_WARNING, "(Ghost.cpp) %c%2i: (%2.1f,%2.1f) -> (%2.1f,%2.1f)", balanceKind == BK_CULTURE ? 'C' : 'I', i,
//			prevPos.x, prevPos.y, getPosition().x, getPosition().y );
						moveDist = 0.0f;
					} else {
//ofLog( OF_LOG_WARNING, "(Ghost.cpp) %c%2i: %.1f >= %.1f; nmd=%.1f", balanceKind == BK_CULTURE ? 'C' : 'I', i, moveDist, pathSegDist, moveDist - pathSegDist );
//ofPoint prevPos = getPosition();
						setPosition( lastPath[i] );
//ofLog( OF_LOG_WARNING, "(Ghost.cpp) %c%2i: (%2.1f,%2.1f) -> (%2.1f,%2.1f)", balanceKind == BK_CULTURE ? 'C' : 'I', i,
//			prevPos.x, prevPos.y, getPosition().x, getPosition().y );
						moveDist -= pathSegDist;
					}
					++i;
				}
			}
		}
//TEMP
//ofLog( OF_LOG_WARNING, "===GHOST POS (%.2f, %.2f) => (%.2f, %.2f)", oldPos.x, oldPos.y, getPosition().x, getPosition().y );
	}
	
	lastGameTime = game.getTime();
}

void Ghost::draw() const {
	ofSetColor( 255, 255, 255 );
	//const ofRectangle& dims = getBBox();
	const ofPoint& pos = getPosition();
	const ofPoint& size = seqPlayer.getSize();
	float dw = size.x * drawSizeFactor;
	float dh = size.y * drawSizeFactor;
	seqPlayer.draw( pos.x - dw / 2, pos.y - dh / 2, dw, dh );
	
	if ( appRef.debugDrawingEnabled ) {
		const Player* p = ( lastNearestPlayer >= 0 ) ? game.getPlayers()[lastNearestPlayer] : 0;
		ofPushStyle();
		ofNoFill();
		ofSetLineWidth( 3 );
		
		int color = balanceKind ? 0xFF7FFF : 0x7FFF7F;
		int r = balanceKind ? 50 : 45;
		ofSetColor( color ); ofCircle( getPosition().x, getPosition().y, r );
		if ( p ) { ofCircle( p->getPosition().x, p->getPosition().y, r ); }
		
//ofLog( OF_LOG_WARNING, "--START path draw" ); //TEMP
		for ( int i = 0; i < lastPath.size(); ++i ) {
			const ofPoint& p1 = ( i == 0 ) ? getPosition() : lastPath[i - 1];
			const ofPoint& p2 = lastPath[i];
//ofLog( OF_LOG_WARNING, "  (%f, %f) -> (%f, %f)", p1.x, p1.y, p2.x, p2.y );
			ofLine(p1.x, p1.y, p2.x, p2.y );
		}
//ofLog( OF_LOG_WARNING, "--END path draw" ); //TEMP
		
		ofPopStyle();
		
		//pathFinder.draw(); //TEMP
		
		GridObject::draw();
	}
}

float Ghost::getBaseSpeed() const { return baseSpeed; }
float Ghost::getSpeedFactor() const { return speedFactor; }
BALANCE_KIND Ghost::getBalanceKind() const { return balanceKind; }

void Ghost::setBaseSpeed( float baseSpeed_ ) { baseSpeed = baseSpeed_; }
void Ghost::setSpeedFactor( float speedFactor_ ) { speedFactor = speedFactor_; }
void Ghost::setBalanceKind( BALANCE_KIND kind_ ) { balanceKind = kind_; }


/*********************
 * PRIVATE FUNCTIONS *
 *********************/

//TEMP
int Ghost::findNearestPlayer( bool onlyCollidables_ ) const {
	const ofPoint& gc = getPosition();
	GameModel::vec_PlayerP players = game.getPlayers();
	float minDist;
	int nearestPlayer = -1;
	
	for ( int i = 0; i < players.size(); ++i ) {
		const ofPoint& pc = players[i]->getPosition();
		const ofPoint& delta = pc - gc;
		float dist = sqrtf( delta.x * delta.x + delta.y * delta.y );
		
		if ( onlyCollidables_ && ! players[i]->isCollidable() ) continue;
		
		if ( nearestPlayer == -1 || dist < minDist ) {
			minDist = dist;
			nearestPlayer = i;
		}
	}
	
	return nearestPlayer;
}
